package thread02;

/**
 * 同步块
 * 语法:
 * synchronized(同步监视器对象){
 * 需要多个线程同步执行的代码片段(多线程会产生并发安全问题的代码)
 * }
 * <p>
 * 有效的缩小同步范围是可以在保证并发安全的前提下尽可能提高并发效率
 */
public class SyncDemo2 {
    public static void main(String[] args) {
        Shop shop = new Shop();
        //Shop shop1 = new Shop();
        //Shop shop2 = new Shop();
        Thread t1 = new Thread("王克晶") {
            public void run() {
                shop.buy();
                //shop1.buy();
            }
        };
        Thread t2 = new Thread("范传奇") {
            public void run() {
                shop.buy();
                //shop2.buy();
            }
        };
        t1.start();
        t2.start();
    }
}

class Shop {
    //在方法上使用synchronized时，同步监视器对象不可选，只能是this
    //public synchronized void buy(){ 将多个线程改为排队执行，虽然不冲突但是效率低，一个线程走完才能走另一个线程
    public void buy() {
        try {
            Thread t = Thread.currentThread(); //获取运行buy方法的线程
            System.out.println(t.getName() + ":正在挑衣服");
            Thread.sleep(5000);

            /*
            同步块可以更精准的控制需要同步执行的代码片段
            但是使用同步块是要指定同步监视器对象，同步监视器对象要同时具备以下条件：
            1:必须是引用类型实例
            2:多个需要同步执行该代码片段的线程看到的必须是同一个对象

            合适的锁对象，应该是在多个线程出现“抢”的时候发挥作用，否则不发挥作用
            例如：两个线程分别执行两个Shop实例的buy方法(相当于两个人去不同的商店)时，那么这两个线程就不发生"抢"的现象,
            此时下面的同步块就不应当要求线程排队执行代码
            如果同步监视器对象选取了字符串字面量(字符串字面量在任何情况下都是同一个对象),那么在
            这样的情况下也要求多个线程排队执行显然是不合适的

            synchronized不是公平锁，当一个线程进入后，如果后续有5个线程陆续执行到这里开始排队时，当进入的线程出了同步块后，
            并不是后5个中先排队的线程先进入，而是后5个线程谁先拿到时间片谁先进入执行
            如果想实现公平锁，可以使用JUC(java.util.concurrent包),java并发包，这个包中包含了很多和并发相关的API
            java.util.concurrent.locks.ReentrantLock 可重入锁可以实现公平锁机制
             */

            //synchronized(new Object()){ //无效的锁对象，因为多个线程看到的不是同一个对象
            //synchronized("hello"){ //有效但不合适，会导致在不应该排队的地方也会排队
            synchronized (this) {
                System.out.println(t.getName() + ":正在试衣服");
                Thread.sleep(5000);
            }

            System.out.println(t.getName() + ":结账离开");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
